#include <ne.h>
#include <system/GlutApplication.h>
using namespace ne;
using namespace std;

GlutApplication *pApplication;

SubMeshPtr convertSubMesh(const bool revert, const string &fileName)
{
    FILE *file = fopen((fileName + ".submeshpy").c_str(), "r");
    if (!file)
    {
        cerr << "Error: Can't find SubMesh file [" << fileName << "]\n";
        return SubMeshPtr();
    }

    Engine *pEngine = Engine::GetSingletonPtr();
    MaterialManager *pMaterialManager = pEngine->getMaterialManager();
    GLBufferObjectManager *pBufferManager = pEngine->getBufferObjectManager();
    char str[MAX_STRING_LENGTH_IN_MESH_FILE];

    fscanf(file, "Name = %s\n", str);
    printf("Name = %s\n", str);
    SubMeshPtr pSubMesh(new SubMesh(str));

    fscanf(file, "MaterialName = %s\n", str);
    printf("MaterialName = %s\n", str);
    if (string(str) != "<>")
    {
        MaterialPtr pMaterial = pMaterialManager->create(str, str);
        pSubMesh->setMaterial(pMaterial);
    }

    VertexDataPtr pVertexData(new VertexData(PT_TRIANGLES));
    pSubMesh->setVertexData(pVertexData);

    int numVertex = 0;
    fscanf(file, "NumVertex = %d\n", &numVertex);
    int numNormal = 0;
    fscanf(file, "NumNormal = %d\n", &numNormal);
    int numTangent = 0;
    fscanf(file, "NumTangent = %d\n", &numTangent);
    int colorSize = 0;
    fscanf(file, "ColorSize = %d\n", &colorSize);
    int numColor = 0;
    fscanf(file, "NumColor = %d\n", &numColor);
    int numSecondaryColor = 0;
    fscanf(file, "NumSecondaryColor = %d\n", &numSecondaryColor);
    int texCoordSize = 0;
    fscanf(file, "TexCoordSize = %d\n", &texCoordSize);
    int numTexCoord = 0;
    fscanf(file, "NumTexCoord = %d\n", &numTexCoord);
    int numBlendCount = 0;
    fscanf(file, "NumBlendCount = %d\n", &numBlendCount);
    int numBlendWeight = 0;
    fscanf(file, "NumBlendWeight = %d\n", &numBlendWeight);
    int numBlendIndex = 0;
    fscanf(file, "NumBlendIndex = %d\n", &numBlendIndex);
    int indexSize = 0;
    fscanf(file, "IndexSize = %d\n", &indexSize);
    int numIndex = 0;
    fscanf(file, "NumIndex = %d\n", &numIndex);
    int numFace = 0;
    fscanf(file, "NumFace = %d\n", &numFace);

    vector<Vector3> *pVertex = 0;
    if (numVertex > 0)
    {
        pVertex = new vector<Vector3>(numVertex);
        for (int i=0; i<numVertex; ++i)
        {
            fscanf(file, "V = %f %f %f\n", &(*pVertex)[i].x, &(*pVertex)[i].y, &(*pVertex)[i].z);
            // cout << "V = " << (*pVertex)[i] << endl;
        }
    }

    vector<Vector3> *pNormal = 0;
    if (numNormal > 0)
    {
        pNormal = new vector<Vector3>(numNormal);
        if (revert)
        {
            for (int i=0; i<numNormal; ++i)
            {
                fscanf(file, "N = %f %f %f\n", &(*pNormal)[i].x, &(*pNormal)[i].y, &(*pNormal)[i].z);
                (*pNormal)[i] = - (*pNormal)[i];
                // cout << "N = " << (*pNormal)[i] << endl;
            }
        }
        else
        {
            for (int i=0; i<numNormal; ++i)
            {
                fscanf(file, "N = %f %f %f\n", &(*pNormal)[i].x, &(*pNormal)[i].y, &(*pNormal)[i].z);
                // cout << "N = " << (*pNormal)[i] << endl;
            }
        }
    }

    vector<Vector3> *pTangent = 0;
    if (numTangent > 0)
    {
        pTangent = new vector<Vector3>(numTangent);
        for (int i=0; i<numTangent; ++i)
        {
            fscanf(file, "T = %f %f %f\n", &(*pTangent)[i].x, &(*pTangent)[i].y, &(*pTangent)[i].z);
            // cout << "T = " << (*pTangent)[i] << endl;
        }
    }
    
    vector<Color> *pColor = 0;
    if (numColor > 0)
    {
        pColor = new vector<Color>(numColor);
        for (int i=0; i<numColor; ++i)
        {
            fscanf(file, "C = %f %f %f %f\n", &(*pColor)[i].r, &(*pColor)[i].g, &(*pColor)[i].b, &(*pColor)[i].a);
            // cout << "C = " << (*pColor)[i] << endl;
        }
    }

    vector<Vector2> *pTexCoord = 0;
    if (numTexCoord > 0)
    {
        pTexCoord = new vector<Vector2>(numTexCoord);
        for (int i=0; i<numTexCoord; ++i)
        {
            fscanf(file, "UV = %f %f\n", &(*pTexCoord)[i].x, &(*pTexCoord)[i].y);
            // cout << "UV = " << (*pTexCoord)[i] << endl;
        }
    }

    vector<Vector4> *pWeight = 0;
    if (numBlendWeight)
    {
        pWeight = new vector<Vector4>(numBlendWeight);
        for (int i=0; i<numBlendWeight; ++i)
        {
            fscanf(file, "W = %f %f %f %f\n", &(*pWeight)[i].x, &(*pWeight)[i].y, &(*pWeight)[i].z, &(*pWeight)[i].w);
            // cout << "W = " << (*pWeight)[i] << endl;
        }
    }

    vector<ne::uint8_t> *pJoint = 0;
    if (numBlendIndex)
    {
        pJoint = new vector<ne::uint8_t>(4 * numBlendIndex);
        for (int i=0; i<numBlendIndex; ++i)
        {
            int x, y, z, w;
            fscanf(file, "J = %d %d %d %d\n", &x, &y, &z, &w);
            (*pJoint)[3 * i] = ne::uint8_t(x);
            (*pJoint)[3 * i + 1] = ne::uint8_t(y);
            (*pJoint)[3 * i + 2] = ne::uint8_t(z);
            (*pJoint)[3 * i + 3] = ne::uint8_t(w);
            // cout << "J = " << (*pJoint)[3 * i] << " " << (*pJoint)[3 * i + 1] << " " << (*pJoint)[3 * i + 2] << " " << (*pJoint)[3 * i + 3] <<endl;
        }
    }

    vector<ne::uint32_t> *pFaceVertex = 0;
    if (numFace > 0)
    {
        pFaceVertex = new vector<ne::uint32_t>(3 * numFace);
        if (revert)
        {
            for (int i=0; i<numFace; ++i)
            {
                fscanf(file, "F = %d %d %d\n", &(*pFaceVertex)[3 * i], &(*pFaceVertex)[3 * i + 2], &(*pFaceVertex)[3 * i + 1]);
                // cout << "F = " << (*pFaceVertex)[3 * i] << " " << (*pFaceVertex)[3 * i + 2] << " " << (*pFaceVertex)[3 * i + 1] << endl;
            }
        }
        else
        {
            for (int i=0; i<numFace; ++i)
            {
                fscanf(file, "F = %d %d %d\n", &(*pFaceVertex)[3 * i], &(*pFaceVertex)[3 * i + 1], &(*pFaceVertex)[3 * i + 2]);
                // cout << "F = " << (*pFaceVertex)[3 * i] << " " << (*pFaceVertex)[3 * i + 1] << " " << (*pFaceVertex)[3 * i + 2] << endl;
            }
        }
    }

    ne::uint32_t bufferSize = 0;
    if (numVertex > 0)
    {
        bufferSize += VertexArray::GetTypeSize(VAT_FLOAT3) * numVertex;
    }
    if (numNormal > 0)
    {
        bufferSize += VertexArray::GetTypeSize(VAT_FLOAT3) * numNormal;
    }
    if (numTangent > 0)
    {
        bufferSize += VertexArray::GetTypeSize(VAT_FLOAT3) * numTangent;
    }
    if (numColor > 0)
    {
        bufferSize += VertexArray::GetTypeSize(VAT_FLOAT4) * numColor;
    }
    if (numTexCoord > 0)
    {
        bufferSize += VertexArray::GetTypeSize(VAT_FLOAT2) * numTexCoord;
    }
    if (numBlendCount > 0)
    {
        bufferSize += VertexArray::GetTypeSize(VAT_FLOAT4) * numBlendWeight;
        bufferSize += VertexArray::GetTypeSize(VAT_BYTE4) * numBlendIndex;
    }

    ne::real *pBuffer = 0;
    ne::uint32_t offset = 0;
    GLBufferObjectPtr pVertexBuffer = pBufferManager->createBufferObject(
            BOT_ARRAY_BUFFER, BOU_STATIC_DRAW, bufferSize);

    if (numVertex > 0)
    {
        ne::uint32_t bitSize = VertexArray::GetTypeSize(VAT_FLOAT3) * numVertex;
        pVertexData->createPositionArray(pVertexBuffer->getSource(), bitSize, offset);
        pBuffer = static_cast<ne::real*>(pVertexBuffer->lock(
            BOA_WRITE_ONLY, offset, bitSize, false));
        for (int i=0; i<numVertex; ++i)
        {
            pBuffer[0] = (*pVertex)[i].x;
            pBuffer[1] = (*pVertex)[i].y;
            pBuffer[2] = (*pVertex)[i].z;
            pBuffer += 3;
        }
        pVertexBuffer->unlock();
        offset += bitSize;
        cout << "Create Vertex\n";
    }

    if (numNormal > 0)
    {
        ne::uint32_t bitSize = VertexArray::GetTypeSize(VAT_FLOAT3) * numNormal;
        pVertexData->createNormalArray(pVertexBuffer->getSource(), bitSize, offset);
        pBuffer = static_cast<ne::real*>(pVertexBuffer->lock(
            BOA_WRITE_ONLY, offset, bitSize, false));
        for (int i=0; i<numNormal; ++i)
        {
            pBuffer[0] = (*pNormal)[i].x;
            pBuffer[1] = (*pNormal)[i].y;
            pBuffer[2] = (*pNormal)[i].z;
            pBuffer += 3;
        }
        pVertexBuffer->unlock();
        offset += bitSize;
        cout << "Create Normal\n";
    }

    if (numTangent > 0)
    {
        ne::uint32_t bitSize = VertexArray::GetTypeSize(VAT_FLOAT3) * numTangent;
        pVertexData->createTangentArray(pVertexBuffer->getSource(), bitSize, offset);
        pBuffer = static_cast<ne::real*>(pVertexBuffer->lock(
            BOA_WRITE_ONLY, offset, bitSize, false));
        for (int i=0; i<numTangent; ++i)
        {
            pBuffer[0] = (*pTangent)[i].x;
            pBuffer[1] = (*pTangent)[i].y;
            pBuffer[2] = (*pTangent)[i].z;
            pBuffer += 3;
        }
        pVertexBuffer->unlock();
        offset += bitSize;
        cout << "Create Tangent\n";
    }

    if (numColor > 0)
    {
        ne::uint32_t bitSize = VertexArray::GetTypeSize(VAT_FLOAT4) * numColor;
        pVertexData->createColorArray(pVertexBuffer->getSource(), bitSize, offset, VAT_FLOAT4);
        pBuffer = static_cast<ne::real*>(pVertexBuffer->lock(
            BOA_WRITE_ONLY, offset, bitSize, false));
        for (int i=0; i<numColor; ++i)
        {
            pBuffer[0] = (*pColor)[i].r;
            pBuffer[1] = (*pColor)[i].g;
            pBuffer[2] = (*pColor)[i].b;
            pBuffer[3] = (*pColor)[i].a;
            pBuffer += 4;
        }
        pVertexBuffer->unlock();
        offset += bitSize;
        cout << "Create Color\n";
    }

    if (numTexCoord > 0)
    {
        ne::uint32_t bitSize = VertexArray::GetTypeSize(VAT_FLOAT2) * numTexCoord;
        pVertexData->createTexCoordArray(pVertexBuffer->getSource(), bitSize, offset, VAT_FLOAT2);
        pBuffer = static_cast<ne::real*>(pVertexBuffer->lock(
            BOA_WRITE_ONLY, offset, bitSize, false));
        for (int i=0; i<numTexCoord; ++i)
        {
            pBuffer[0] = (*pTexCoord)[i].x;
            pBuffer[1] = (*pTexCoord)[i].y;
            pBuffer += 2;
        }
        pVertexBuffer->unlock();
        offset += bitSize;
        cout << "Create TexCoord\n";
    }

    if (numBlendCount > 0)
    {
        ne::uint32_t bitSize = VertexArray::GetTypeSize(VAT_FLOAT4) * numBlendWeight;
        pVertexData->createVertexArray(pVertexBuffer->getSource(), bitSize, offset, VAS_BLEND_WEIGHT, VAT_FLOAT4);
        pBuffer = static_cast<ne::real*>(pVertexBuffer->lock(
            BOA_WRITE_ONLY, offset, bitSize, false));
        for (int i=0; i<numBlendWeight; ++i)
        {
            pBuffer[0] = (*pWeight)[i].x;
            pBuffer[1] = (*pWeight)[i].y;
            pBuffer[2] = (*pWeight)[i].z;
            pBuffer[3] = (*pWeight)[i].w;
            pBuffer += 4;
        }
        pVertexBuffer->unlock();
        offset += bitSize;
        cout << "Create Weight\n";

        bitSize = VertexArray::GetTypeSize(VAT_BYTE4) * numBlendIndex;
        pVertexData->createVertexArray(pVertexBuffer->getSource(), bitSize, offset, VAS_BLEND_INDEX, VAT_BYTE4);
        ne::uint8_t *piBuffer = static_cast<ne::uint8_t*>(pVertexBuffer->lock(
            BOA_WRITE_ONLY, offset, bitSize, false));
        for (int i=0; i<numBlendIndex; ++i)
        {
            piBuffer[0] = (*pJoint)[3 * i];
            piBuffer[1] = (*pJoint)[3 * i + 1];
            piBuffer[2] = (*pJoint)[3 * i + 2];
            piBuffer[3] = (*pJoint)[3 * i + 3];
            piBuffer += 4;
        }
        pVertexBuffer->unlock();
        offset += bitSize;
        cout << "Create Joint Index\n";
    }

    if (indexSize == 4)
    {
        bufferSize = numFace * VertexArray::GetTypeSize(VAT_UNSIGNED_INT3);
        GLBufferObjectPtr pIndexBuffer = pBufferManager->createBufferObject(
                BOT_ELEMENT_ARRAY_BUFFER, BOU_STATIC_DRAW, bufferSize);
        if (numIndex > 0)
        {
            ne::uint32_t bitSize = numFace * VertexArray::GetTypeSize(VAT_UNSIGNED_INT3);
            pVertexData->createIndexArray(pIndexBuffer->getSource(), bitSize, 0, VAT_UNSIGNED_INT);
            ne::uint32_t *piBuffer = static_cast<ne::uint32_t*>(pIndexBuffer->lock(
                BOA_WRITE_ONLY, 0, bitSize, false));
            for (int i=0; i<numFace; ++i)
            {
                piBuffer[0] = (*pFaceVertex)[3 * i];
                piBuffer[1] = (*pFaceVertex)[3 * i + 1];
                piBuffer[2] = (*pFaceVertex)[3 * i + 2];
                piBuffer += 3;
            }
            pIndexBuffer->unlock();
            cout << "Create Int Index\n";
        }
    }
    else if (indexSize == 2)
    {
        bufferSize = numFace * VertexArray::GetTypeSize(VAT_UNSIGNED_SHORT3);
        GLBufferObjectPtr pIndexBuffer = pBufferManager->createBufferObject(
                BOT_ELEMENT_ARRAY_BUFFER, BOU_STATIC_DRAW, bufferSize);
        if (numIndex > 0)
        {
            ne::uint32_t bitSize = numFace * VertexArray::GetTypeSize(VAT_UNSIGNED_SHORT3);
            pVertexData->createIndexArray(pIndexBuffer->getSource(), bitSize, 0, VAT_UNSIGNED_SHORT);
            ne::uint16_t *piBuffer = static_cast<ne::uint16_t*>(pIndexBuffer->lock(
                BOA_WRITE_ONLY, 0, bitSize, false));
            for (int i=0; i<numFace; ++i)
            {
                piBuffer[0] = ne::uint16_t((*pFaceVertex)[3 * i]);
                piBuffer[1] = ne::uint16_t((*pFaceVertex)[3 * i + 1]);
                piBuffer[2] = ne::uint16_t((*pFaceVertex)[3 * i + 2]);
                piBuffer += 3;
            }
            pIndexBuffer->unlock();
            cout << "Create Short Index\n";
        }
    }
    else if (indexSize == 1)
    {
        bufferSize = numFace * VertexArray::GetTypeSize(VAT_BYTE3);
        GLBufferObjectPtr pIndexBuffer = pBufferManager->createBufferObject(
                BOT_ELEMENT_ARRAY_BUFFER, BOU_STATIC_DRAW, bufferSize);
        if (numIndex > 0)
        {
            ne::uint32_t bitSize = numFace * VertexArray::GetTypeSize(VAT_BYTE3);
            pVertexData->createIndexArray(pIndexBuffer->getSource(), bitSize, 0, VAT_BYTE3);
            ne::uint8_t *piBuffer = static_cast<ne::uint8_t*>(pIndexBuffer->lock(
                BOA_WRITE_ONLY, 0, bitSize, false));
            for (int i=0; i<numFace; ++i)
            {
                piBuffer[0] = ne::uint8_t((*pFaceVertex)[3 * i]);
                piBuffer[1] = ne::uint8_t((*pFaceVertex)[3 * i + 1]);
                piBuffer[2] = ne::uint8_t((*pFaceVertex)[3 * i + 2]);
                piBuffer += 3;
            }
            pIndexBuffer->unlock();
            cout << "Create Short Index\n";
        }
    }

    delete pVertex;
    delete pNormal;
    delete pTangent;
    delete pColor;
    delete pTexCoord;
    delete pWeight;
    delete pJoint;
    delete pFaceVertex;

    fclose(file);
    return pSubMesh;
}

bool convertMesh(const bool revert, const string &inFile)
{
    FILE *file = fopen(inFile.c_str(), "r");
    if (!file)
    {
        cerr << "Error: Can't find Mesh file [" << inFile << "]\n";
        return false;
    }

    Engine *pEngine = Engine::GetSingletonPtr();
    TextureManager *pTextureManager = pEngine->getTextureManager();
    char str[MAX_STRING_LENGTH_IN_MESH_FILE];

    fscanf(file, "Name = %s\n", str);
    printf("Name = %s\n", str);

    string outFile = str;
    outFile += ".mesh";

    MeshPtr pMesh = MeshPtr(new Mesh(0, 0, str, outFile, false, 0));
    pMesh->load();

    fscanf(file, "TextureName = %s\n", str);
    printf("TextureName = %s\n", str);
    if (string(str) != "<>")
    {
        TexturePtr pTexture = pTextureManager->createTextureFromFile(str, str, TF_RGBA);
        pMesh->setTexture(pTexture);
    }

    fscanf(file, "BumpMapName = %s\n", str);
    printf("BumpMapName = %s\n", str);
    if (string(str) != "<>")
    {
        TexturePtr pTexture = pTextureManager->createTextureFromFile(str, str, TF_RGBA);
        pMesh->setBumpMap(pTexture);
    }

    int numSubMeshs = 0;
    fscanf(file, "NumSubMeshs = %d\n", &numSubMeshs);
    printf("NumSubMeshs = %d\n", numSubMeshs);
    for (int i=0; i<numSubMeshs; ++i)
    {
        fscanf(file, "SM = %s\n", str);
        printf("Convert SubMesh [%s] ... ...\n", str);
        SubMeshPtr pSubMesh = convertSubMesh(revert, str);
        pMesh->addChild(pSubMesh);
        printf("[Ok]\n");
    }

    pMesh->download();
    printf("========== Finished convert\n");
    fclose(file);
    return true;
}

int main(int argc, char *argv[])
{
    pApplication = new ne::GlutApplication();

    if (argc == 2)
    {
        convertMesh(false, argv[1]);
    }
    else if (argc > 2)
    {
        convertMesh(true, argv[1]);
    }

    delete pApplication;

    return 0;
}
